package stocc.xml;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import stocc.ParserOutputHandler;
import stocc.logger.Level;
import stocc.logger.Logger;
import stocc.subject.Member;
import stocc.subject.Method;
import stocc.subject.Subject;
import stocc.subject.View;

public class ToXML implements ParserOutputHandler{

	List<Subject> subjects;
	String filename;
	Document doc;
	
	public ToXML(String filename){
		this.filename = filename;
		subjects = new ArrayList<Subject>();
		try{
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		DocumentBuilder db = dbf.newDocumentBuilder();
		doc = db.newDocument();
		}catch (Exception e){
			log(e.getMessage(), Level.CRITICAL);
		}
		
	}
	
	@Override
	public void addSubject(Subject subject) {
		subjects.add(subject);
		
	}

	@Override
	public void done() throws Exception{
		doXMLOutput();
		
	}
	
	private void doXMLOutput() 
	 throws TransformerConfigurationException, TransformerException{
		Element root = doc.createElement("root");
		doc.appendChild(root);
		for(Subject s : subjects){
			addSubjectToTree(root, s);
		}
		
		Transformer tf = TransformerFactory.newInstance().newTransformer();
		Source source = new DOMSource(doc);
		Result result = new StreamResult(new File(filename));
		tf.transform(source, result);
		
	}
	
	private void addSubjectToTree(Element node, Subject subject){
		//LOG
		log("Adding subject: " + subject.getName(), Level.DEBUG);
		
		Element subElem = doc.createElement("subject");
		subElem.setAttribute("name", subject.getName());
		for(View v : subject.getViews()){
			addViewToTree(subElem, v);
		}
		node.appendChild(subElem);
	}
	
	private void addViewToTree(Element node, View view){
		//LOG
		log("Adding view: " + view.getName(), Level.DEBUG);
		
		Element viewElem = doc.createElement("view");
		viewElem.setAttribute("name", view.getName());
		viewElem.setAttribute("visibility", view.getVisibility().toString());
		
		for(String imp : view.getImports()){
			Element impElem = doc.createElement("view");
			impElem.setTextContent(imp);
			viewElem.appendChild(impElem);
		}
		
		if(view.getSettings().onNameConflict != null){
			viewElem.setAttribute("OnNameConflict", view.getSettings().onNameConflict.toString());
		}
		for(Member m : view.getMembers()){
			addMemberToTree(viewElem, m);
		}
		for(Method me : view.getMethods()){
			addMethodToTree(viewElem, me);
		}
		node.appendChild(viewElem);
	}
	
	private void addMemberToTree(Element node, Member member){
		Element memElem = doc.createElement("member");
		memElem.setAttribute("name", member.getName());
		memElem.setAttribute("visibility", member.getVisibility().toString());
		memElem.setAttribute("type", member.getType());
		node.appendChild(memElem);
	}
	
	private void addMethodToTree(Element node, Method method){
		Element metElem = doc.createElement("method");
		metElem.setAttribute("name", method.getName());
		metElem.setAttribute("visibility", method.getVisibility().toString());
		metElem.setAttribute("returnType", method.getReturnType());
		metElem.setTextContent(method.getCode());
		node.appendChild(metElem);
	}
	/**
	 * Logging method, add this to any class that does logging
	 * @param message The message to be logged
	 * @param level The logging level of the message
	 */
	private static void log(String message, Level level){
		Logger.getLogger().log(level, "ToXML", message);
	}	
	
}
